<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
<style>
    .main {
        height: 600px;
        width: 600px;
        margin: 50px auto;
    }
    #tip{
            position: absolute;
            margin-left: 10px;
            margin-top: 30px;
            line-height: 22px;
            background-color: rgba(0,0,0,0.6);
            padding: 4px 9px;
            font-size: 13px;
            color: #fff;
            border-radius: 3px;
            pointer-events:none;
            display: none;
            transition:all  cubic-bezier(0.075, 0.82, 0.165, 1)  1s;
        }
</style>
</head>

<body>
    <div class="main"></div>
    <canvas id="ca1"></canvas>
</body>
<script type="module">

    const canvas = document.getElementById('ca1');
    const ctx = canvas.getContext('2d');
    canvas.width = 500 ;
    canvas.height = 500 ;
    ctx.beginPath();
    ctx.fillStyle='red';
    ctx.lineWidth = 1 ;
    ctx.strokeStyle = '#001';
    // 圆心 cx   cy    r   起始角 结束角  是否逆时针画圆（数值和正常的直角坐标系相同）
    ctx.arc( 100, 100, 50, 0, Math.PI / 2, true);
    ctx.moveTo(105,100);
    ctx.arc( 100, 100, 5, 0, Math.PI * 2, false);
    ctx.stroke();

    ctx.beginPath();
    ctx.setLineDash([10,3,1,3,10])
    ctx.moveTo(100, 150);
    ctx.strokeStyle = 'red'; // 中途换颜色直接被覆盖
    //           x1 y1   x2 y2    r
    ctx.arcTo(150, 200, 100, 300, 20 )

    ctx.stroke();
    
    import "../../assets/js/d3/d3.min.js" ;

    const category = ['html', 'css', 'javascript']
    const series = ['学习人数', '就业人数']
/*
*
*
*/ 
const source=[
        //html css js
        [30,   20, 40],//学习人数
        [40,   30, 50]//就业人数
    ];
    const source1 = source.map( (item, i) => {
       const seriesName =series[i];
       const seriesData  = item ;
       return item.map( ( rectData,  rectNum) =>  {
           return {seriesData, seriesName, rectNum,rectData,seriesNum:i, rectName:category[rectNum]}}
       )
    })
    console.log(source1)
    const wraper = d3.select('.main') ;

    const [width, height]  = [600 , 600];
const svg = wraper.append('svg')
.attr('version', 1.2)
.attr('xmlns', 'http://www.w3.org/2000/svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`) ;

const len= category.length ;
    /*用range()方法，基于类目数量，获取x轴的在图表坐标系中的数据 xChartData，如[0,1,2]*/
const xChartData  = d3.range(len) ;
console.log(xChartData);// 结果是从0 到 len的整数集合

const maxY = Math.max(...source.flat());
const yChartRange = [0, maxY];
/* 
*x轴在像素坐标内的起始点和结束点，
 */

 const xPixielRange = [50, width -50];
 const yPixielRange = [ height -50, 50]; // 起点在下面
 const colors=['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83',  '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'];

 /* 
 *建立x轴比例尺
* 用scaleBand()方法建立分段比例尺 xScale
* 用scaleLinear()方法建立线性比例尺 yScale
* padding 设置间隔 百分比
* 用domain()方法在比例尺中写入图表数据xChartData
* 用rangeRound()方法在比例尺中写入像素数据，即像素的起始位和结束位xPixelRange
    * 用padding()方法设置类目的内边距，百分比单位，如0.1

*/
  const xScale = d3.scaleBand()
  .domain(xChartData)
  .padding(.1)
  .rangeRound(xPixielRange);

  const yScale = d3.scaleLinear()
  .domain(yChartRange) 
  .range(yPixielRange);
  /* 建立x轴对象 */

  const xAxisGenerator = d3.axisBottom(xScale);
  const yAxisGenerator = d3.axisLeft(yScale); //刻度朝左
  
/*利用坐标轴生成器绘制坐标轴
    *   在svg中append 加入g 对象
    *   用attr() 方法设置transform 属性中的translate位置
    *   用call()方法调用xAxisGenerator轴生成器，生成坐标轴
    *   用selectAll()方法选择所有的text文本
    *   用text()方法将图表数据设置为类目数据
    *   用style()方法设置字体大小
    * */
  svg.append('g')
  .attr('transform', `translate(0 ${height - 50})`)
  .call(xAxisGenerator)
  .selectAll('text')
  .text((le, i) => category[i])
  .style('font-size', '12px');

  svg.append('g')
  .attr('transform', `translate(50 0)`)
  .call(yAxisGenerator)
  .selectAll('text')
  .attr('font-size', '12px');

  const xBandW = xScale.bandwidth() // 获取一个类目的像素宽
  const n = source.length // 系列的数量
  const colW = xBandW / n //单个系列的宽度
  const colorLen = colors.length // 
  /* 
  *建立系列集合  ，包含系列对象
  */
 const seriesObjs = svg.append('g')
 .selectAll('g')
 .data(source1)
 .join('g')
 .attr('transform',(series, index) => `translate(${index * colW})`)
 .attr('fill', (item, index) => colors[index % colorLen]);
 console.log(seriesObjs)

 /* 
 * 建立柱状体集合
  */
 const rects = seriesObjs.selectAll('rect')
 .data( item => item)
 .join('rect')
 .classed('item', true);

 class EaseObj{
        /*构造函数*/
        constructor(target){
            this.target=target
            this.fm=0
            this.pos={x:0,y:0}
            this.endPos={x:0,y:0}
            this.ratio=0.1
            this._play=false
        }
        /*play 属性的取值器*/
        get play(){
            return this._paly
        }
        /*play 属性的赋值器
        *   现在的值不等于当过去值时
        *       当现在的值为true时
        *           缓动跟随
        *           更新目标对象
        *           连续渲染
        *       当现在的值为false时
        *           删除动画帧，取消连续渲染
        * */
        set play(val){
            if(val!==this._play){
                if(val){
                    this.render()
                }else{
                    this.cancel()
                }
                this._play=val
            }
        }

        /*render 渲染方法
        *   按比值，让pos位置接近终点endPos
        *   更新目标对象target的样式
        *   连续渲染
        * */
        render(){
            const {target,pos,endPos,ratio}=this
            pos.x+=(endPos.x-pos.x)*ratio
            pos.y+=(endPos.y-pos.y)*ratio
            target.style('left',`${pos.x}px`)
                .style('top',`${pos.y}px`)
            this.fm=requestAnimationFrame(()=>{
                this.render()
            })
        }

        /*cancel 删除动画帧，取消连续渲染*/
        cancel(){
            cancelAnimationFrame(this.fm)
        }
    }
 /* 
 *设定柱形样式尺寸
  */
 

 rects.attr('x',({rectData}, index)=> xScale(index))
 .attr('width', colW)
 /* 初始动画 */
 rects.attr('y', ()=> yScale(0))
 .attr('height', 0);

 rects.transition()
 .duration(1000)
 .ease(d3.easeBounce)
 .delay( ({rectNum, seriesNum})=> (rectNum + seriesNum)* 200)
.attr('y', ({rectData}) =>  yScale(rectData))
.attr('height', ({rectData}) => yScale(0) - yScale(rectData));
/* 
*提示 
*/
const tip  = wraper.append('div')
.attr('id', 'tip');

//  缓动对象
// const easeTip = new EaseObj(tip);

rects.on('mouseenter', ({clientX,clientY}, data)=> {
    const {rectData,seriesName,rectName} = data ;
    console.log(data)
    tip.style('display', 'block')
    .style('left',`${clientX}px`)
    .style('top',`${clientY}px`)
    .html(`<p>${seriesName}</p>
    <p>${rectData}</P>`)
})
rects.on('mousemove', ({clientX,clientY}) => {
    tip
    .style('left',`${clientX}px`)
    .style('top',`${clientY}px`)
})
rects.on('mouseleave', ()=> {
    tip.style('display', 'none')
})

</script>
</html>